package com.github.eclipseace.mybatis.spring;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;

/**
 * @author eclipseAce
 */
public class ClassPathScanner {
	private final Environment environment;
	private final MetadataReaderFactory metadataReaderFactory;
	private final ResourcePatternResolver resourcePatternResolver;
	private final List<TypeFilter> typeFilters = new ArrayList<>();

	public ClassPathScanner(ResourceLoader resourceLoader, Environment environment) {
		this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
		this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
		this.environment = environment != null ? environment : new StandardEnvironment();
	}

	protected boolean isTypeMatch(MetadataReader metadataReader) throws IOException {
		for (TypeFilter typeFilter : typeFilters) {
			if (!typeFilter.match(metadataReader, metadataReaderFactory)) {
				return false;
			}
		}
		return true;
	}

	public void addTypeFilter(TypeFilter typeFilter) {
		typeFilters.add(typeFilter);
	}

	public void addTypeFilters(List<? extends TypeFilter> typeFilters) {
		this.typeFilters.addAll(typeFilters);
	}

	public List<MetadataReader> scan(String pkg) throws IOException {
		List<MetadataReader> metadataReaders = new ArrayList<>();
		String path = ClassUtils.convertClassNameToResourcePath(environment.resolveRequiredPlaceholders(pkg));
		String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + path + "/**/*.class";
		for (Resource resource : resourcePatternResolver.getResources(pattern)) {
			if (resource.isReadable()) {
				MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
				if (isTypeMatch(metadataReader)) {
					metadataReaders.add(metadataReader);
				}
			}
		}
		return metadataReaders;
	}
}
